/**********************  DOEVENTS MACRO *************************
*                                                              *
*  This macro generates a table of summary statistics relating  *
*  to the number of events                                     *                                                   *                                                              *
*                                                              *
****************************************************************/

%macro doevents(var=,  /* list of variables containing event descriptions */
                any=Y,
                anydesc=ANY EVENT,
                total=N,
                tothead=Total,
                eventlbl=,
                split=%str(*),
                case=,               /* can be UPPER, LOWER, or UPPER1 */
                pagenum=Y,           /* add page x of y to footer */
                spacing=2,           /* spacing between columns */
                by=,  /* add {P} or {E} after var to indicate if the variable originates from the events or pop dataset */
                column=,             /* column (e.g. treatment) variable */
                subj=,               /* subject variable */
                flow=,               /* variables to add FLOW proc report option */
                type=BOTH,           /* can be BOTH, PATIENT or EVENT */
                colorder=INTERNAL,   /* can be FORMATTED or INTERNAL */
                order=FREQ,          /* can be either FREQ or ALPHA */
                addn=Y,              /* adds N= into the column heading */
                skip=1,              /* put the skip _level_ */
                outfile=,            /* output file name */
                filetype=ASCII,          /* ASCII or HTML or PDF or RTF */
                gencode=,       /* filename for generated code */
                data=_last_,    /* dataset containing events */
                dwhere=,        /* where clause applied to events data */
                popdata=,       /* population dataset */
                pwhere=,        /* where clause applied to pop dataset */
                lasthead=,           /* the last line of the heading that indicates, n % and # */
                pctfmt=5.1,     /* format for percentages */
                pefmt=,         /* format for patient-event counts */
                evfmt=,         /* format for event counts */
                outds=          /* output dataset */) ;

options nomlogic mprint nosymbolgen;

%let debug = N;

%if %upcase(&filetype) ne HTML 
  and %upcase(&filetype) ne PDF 
  and %upcase(&filetype) ne RTF 
  and %upcase(&filetype) ne ASCII %then %put WARNING: Filetype option not recognised.  ASCII file will be produced;

%if "&gencode" ne "" %then %do;
  filename gencode "&gencode";
%end;

%let colorder=%upcase(&colorder);               
%let order=%upcase(&order);               
%let type=%upcase(&type);               
%let total=%upcase(&total);

%*** put all the requested by variables into individual macro variables ***;
%let bycnt=1;
%let wordcnt=1;
%let word = %scan(&by,&wordcnt,%str( ));
%do %while(&word~=);
  %let by&bycnt=%upcase(&word);
  %let word = %scan(&by,%eval(&wordcnt+1),%str( ));
  %if "%upcase(&word)"="{P}" %then %do;
    %let bys&bycnt=P;
    %let wordcnt=%eval(&wordcnt+1);
  %end;
  %else %if "%upcase(&word)"="{E}" %then %do;
    %let bys&bycnt=D;
    %let wordcnt=%eval(&wordcnt+1);
  %end;
  %else %let bys&bycnt=;
  %let bycnt=%eval(&bycnt+1);
  %let wordcnt=%eval(&wordcnt+1);
  %let word = %scan(&by,%eval(&wordcnt),%str( ));
%end;
%let bycnt = %eval(&bycnt-1);

%*** reset the by variable to exclude the {E} and {P};
%let by = ;
%do i = 1 %to &bycnt;
  %let by = &by &&by&i;
%end;

%*** put all the variables into individual macro variables ***;
%let varcnt=1;
%let word = %scan(&var,&varcnt,%str( ));
%do %while(&word~=);
  %let var&varcnt=%upcase(&word);
  %let varcnt=%eval(&varcnt+1);
  %let word = %scan(&var,%eval(&varcnt),%str( ));
%end;
%let varcnt = %eval(&varcnt-1);

%*** put all the variables into individual macro variables ***;
%let colcnt=1;
%let wordcnt=1;
%let word = %scan(&column,&wordcnt,%str( ));
%do %while(&word~=);
  %let col&colcnt=%upcase(&word);
  %let word = %scan(&column,%eval(&wordcnt+1),%str( ));
  %if "%upcase(&word)"="{P}" %then %do;
    %let cols&colcnt=P;
    %let wordcnt=%eval(&wordcnt+1);
  %end;
  %else %if "%upcase(&word)"="{E}" %then %do;
    %let cols&colcnt=D;
    %let wordcnt=%eval(&wordcnt+1);
  %end;
  %else %let cols&colcnt=;
  %*put Test 2 : word = &word : colcnt = &colcnt : wordcnt = &wordcnt : col&colcnt = &&col&colcnt : cols&colcnt = &&cols&colcnt;
  %*put Word number &wordcnt is &word;
  %let colcnt=%eval(&colcnt+1);
  %let wordcnt=%eval(&wordcnt+1);
  %let word = %scan(&column,&wordcnt,%str( ));
  %*put Test 3 : word = &word : colcnt = &colcnt : wordcnt = &wordcnt : col&colcnt = &&col&colcnt : cols&colcnt = &&cols&colcnt;
%end;
%let colcnt = %eval(&colcnt-1);

%let docheck=0;
%do i=1 %to &colcnt;
  %if "&&cols&i" = "" %then %let docheck = 1;
  %*put Word number &i is &&col&i and has source &&cols&i;
%end;

%* check where the non-specified column and by variables come from;
%if &docheck %then %do;

  proc contents data=&popdata noprint out=_check1_ (keep=name);
  run;

  data _null_;
    set _check1_;
    %do i = 1 %to &colcnt;
      %if "&&cols&i" = "" %then %do;
        if upcase(name)="%upcase(&&col&i)" then call symput("COLS&i","P");
      %end;
    %end;
  run;

  data _null_;
    set _check1_;
    %do i = 1 %to &bycnt;
      %if "&&bys&i" = "" %then %do;
        if upcase(name)="%upcase(&&by&i)" then call symput("BYS&i","P");
      %end;
    %end;
  run;

  proc contents data=&data noprint out=_check2_ (keep=name);
  run;

  data _null_;
    set _check2_;
    %do i = 1 %to &colcnt;
      %if "&&cols&i" = "" %then %do;
        if upcase(name)="%upcase(&&col&i)" then call symput("COLS&i","D");
      %end;
    %end;
  run;

  data _null_;
    set _check2_;
    %do i = 1 %to &bycnt;
      %if "&&bys&i" = "" %then %do;
        if upcase(name)="%upcase(&&by&i)" then call symput("BYS&i","D");
      %end;
    %end;
  run;

%end;

%* add in the column variable from the pop dataset;
proc sort data=&data out=_xxdata_ (keep=&var &subj %do i=1 %to &colcnt;
                                                         %if &&cols&i=D %then &&col&i;
                                                         %if &bycnt>0 %then %do;
                                                           %if &&bys&i=D %then &&by&i;
                                                         %end;
                                                       %end;);
  by &subj;
  %if "&dwhere" ne "" %then where &dwhere;;
proc sort data=&popdata nodupkey out=_ppdata_ (keep=&subj %do i=1 %to &colcnt;
                                                         %if &&cols&i=P %then &&col&i;
                                                         %if &bycnt>0 %then %do;
                                                           %if &&bys&i=P %then &&by&i;
                                                         %end;
                                                       %end;);
  by &subj;
  %if "&pwhere" ne "" %then where &pwhere;;
run;

data _xxdata_;
  merge _ppdata_ (in=inpop) _xxdata_ (in=indata);
  by &subj;
  if inpop;
  %*** flag the events so that when counting the columns we dont count missings;
  if indata then _datflg_ = 1;
  else _datflg_ = 0;
run;

%if %length(&gencode) > 0 %then %do;
  data _null_;
    file gencode;
    put "**************************************************;";
    put "* Sort the Events Dataset and Population Dataset *;";
    put "**************************************************;";
    put "proc sort data=&data out=_xxdata_ (keep=&var &by &subj " @;
    %do i=1 %to &colcnt;
      %if &&cols&i=D %then %do;
        put "&&col&i " @;
      %end;
    %end;
    put ");";
    put "  by &subj;";
    %if &dwhere ne %then %do;
      put "  where &dwhere;";
    %end;
    put "proc sort data=&popdata nodupkey out=_ppdata_ (keep=&subj " @;
    %do i=1 %to &colcnt;
      %if &&cols&i=P %then %do;
        put "&&col&i " @;
      %end;
    %end;
    put ");";
    put "  by &subj;";
    %if &pwhere ne %then %do;
      put "  where &pwhere;";
    %end;
    put "run;";
    put;
    put "************************************************************;";
    put "* Merge the Events Dataset and Population Dataset Together *;";
    put "************************************************************;";
    put "data _xxdata_;";
    put "  merge _ppdata_ (in=inpop) _xxdata_ ;";
    put "  by &subj;";
    put "  if inpop;";
    put "run;";
    put;
  run;
%end;	

%*** store all the variable formats and labels ***;
proc contents data=_xxdata_ noprint out=kkzz0001 (keep=name type length label format formatl formatd just);
run;

%if &debug=Y %then %do;
  proc print;
    title "proc contents output";
  run;
%end;

data _null_;
  set kkzz0001;
  length kformat $15 ;
  if format = '$' then kformat = '$'||compress(put(formatl,7.0))||'.';
  else if format ne '' then kformat = compress(format)||'.';
  else if formatl > 0 then kformat = compress(put(formatl,best.))||'.'||compress(put(formatd,best.));
  %*** get formats and labels for analysis variables ***;
  %do i=1 %to &varcnt;
    if compress(upcase(name)) = "%upcase(&&var&i)" then do;
      call symput("vrlb&i",trim(left(label)));
      call symput("vrft&i",compress(kformat));
      call symput("vrfl&i",compress(put(formatl,best.)));
      call symput("vrfd&i",compress(put(formatd,best.)));
      call symput("vrty&i",type);
    end;
  %end;
  %*** get formats and labels for by variables ***;
  %do i=1 %to &bycnt;
    if compress(upcase(name)) = "%upcase(&&by&i)" then do;
      call symput("bylb&i",trim(label));
      call symput("byft&i",compress(kformat));
    end;
  %end;
  %*** get the label for the column variables ***;
  %do i=1 %to &colcnt;
    if compress(upcase(name)) = %upcase("&&col&i") then do;
      if compress(label) ne '' then call symput("collb&i",trim(label)); 
      else call symput("collb&i",trim(name));
      call symput("colfmt&i",trim(kformat));
      call symput("coltp&i",trim(type));
    end;
  %end;
run;

%do i=1 %to &varcnt;
  %if "&&vrlb&i" = "" or "&&vrlb&i" = " "  %then %let vrlb&i = &&var&i;
  %if &debug=Y %then %do;
    %put variable number &i has no label so setting it to variable name: &&var&i;
  %end;
%end;

%* count the number of columns the report will have - 
   reports with by statements will all have the same
   number of columns;
proc sort data=_xxdata_;
  by %do i=1 %to &colcnt;
       &&col&i
     %end;;
run;

data countcol;
  set _xxdata_;
  by %do i=1 %to &colcnt;
       &&col&i
     %end;;
  if last.&&col&colcnt then output;
  keep %do i=1 %to &colcnt;
         &&col&i
       %end;;
run;

%if &debug=Y %then %do;
  proc print;
    title "data countcol - used to work out how many columns there will be";
  run;
%end;


data _null_;
  set countcol end=last;
  by %do i=1 %to &colcnt;
       &&col&i
     %end;;
  retain colcnt 1;
  %do i=1 %to &colcnt;
    %if &&colfmt&i = %then %do;
      call symput("cv&i._"||compress(put(colcnt,8.0)),trim(&&col&i));
      call symput("cl&i._"||compress(put(colcnt,8.0)),trim(&&col&i));
    %end;
    %else %do;
      call symput("cv&i._"||compress(put(colcnt,8.0)),trim(&&col&i));
      %* call symput("cv&i._"||compress(put(colcnt,8.0)),trim(put(&&col&i,&&colfmt&i..)));
      call symput("cl&i._"||compress(put(colcnt,8.0)),trim(put(&&col&i,&&colfmt&i..)));;
    %end;
  %end;
  colcnt=colcnt+1;
  if last then call symput("numcols",colcnt-1);
run;

%do i=1 %to &numcols;
  %put Column &i has &colcnt levels :;
  %do j=1 %to &colcnt;
    %if "%trim(&&cv&j._&i)" = "" %then %do;
      %let cv&j._&i = Missing;
      %if "%trim(&&cl&j._&i)" = "" %then %let cl&j._&i = Missing;
    %end;
    %put Level &j has value &&cv&j._&i and label &&cl&j._&i;
  %end;
%end;

%* count the number of patients to use as denominators for %s;
proc sort data=_xxdata_ nodupkey out=_den1_;
  by &by %do i=1 %to &colcnt;
           &&col&i
         %end; &subj;
run;

data _denom1;
  retain _ndenom_;
  set _den1_;
  by &by %do i=1 %to &colcnt;
           &&col&i
         %end; &subj;
  if first.&&col&colcnt then _ndenom_ = 0;
  if first.&subj then _ndenom_ = _ndenom_ + 1;
  if last.&&col&colcnt then output;
  keep &by %do i=1 %to &colcnt;
           &&col&i
         %end; _ndenom_;
run;

%if &debug = Y %then %do;
  proc print;
    title "denominator data";
  run;
%end;

data _denom2;
  set _denom1 %if "&by"="" %then end=last;;
  %if "&by" ne "" %then %do;
    by &by;
  %end;
  retain
  %do j=1 %to &numcols;
    _den&j
  %end; 0;
  %do j=1 %to &numcols;
    if %do k=1 %to &colcnt;
         &&col&k = %if &&coltp&k=1 %then &&cv&k._&j;
                   %else %if "&&cv&k._&j"="Missing" %then "";
                   %else "&&cv&k._&j"; 
         %if &k ne &colcnt %then and;
       %end;
    then _den&j=_ndenom_;
  %end;
  _dummy = 'XXX';
  keep &by
  %do j=1 %to &numcols;
    _den&j
  %end; _dummy;
  %if "&by" = "" %then %do;
    if last then do;
      _totden_ = 
      %do i=1 %to &numcols;
        _den&i %if &i ne &numcols %then +;
      %end;;
      output;
      %do i=1 %to &numcols;
        call symput("KKCNUM&i",compress(put(_den&i,10.0)));
      %end;
      call symput("TOTDENOM",compress(put(_totden_,10.0)));
    end;
  %end;
  %else %do;
    if last.&&by&bycnt then do;
      _totden_ = 
      %do i=1 %to &numcols;
        _den&i %if &i ne &numcols %then +;
      %end;;
      output;
    end;
  %end;
run;

%*do i=1 %to &numcols;
  %*put The denominator to be used for column &i is &&kkcnum&i;
%*end;

%* add in the denominator info to the analysis dataset;
data _xxdata_;
  set _xxdata_;
  _dummy = 'XXX';
proc sort data=_xxdata_;
  by &by _dummy;
run;

%****************************************************;
%*** Merge the denominator data with the dataset  ***;
%*** Also remove any event observations that only ***;
%*** have missing data                            ***;
%****************************************************;
data _xxdata_;
  %if &colorder=FORMATTED %then %do;
    %do i=1 %to &colcnt;
      length cltmpx&i $100;
    %end;
  %end;
  merge _xxdata_ _denom2;
  by &by _dummy;
  %* create temporary variables to use for the formatted value of the columns;
  %if &colorder=FORMATTED %then %do;
    %do i=1 %to &colcnt;
      %if &&colfmt&i ne %then %do;
        cltmpx&i = put(&&col&i,&&colfmt&i);
      %end;
      %else %do;
        cltmpx&i = &&col&i;
      %end;
    %end;
  %end;
  %* remove observations where the event data is missing;
  if %do i=1 %to &varcnt;
       %if &i > 1 %then and;
       %if &&vrty&i=1 %then &&var&i = . ;
	   %else %if &&vrty&i=2 %then compress(&&var&i) = '';
     %end;
	 then delete;
run;
%if &debug=Y %then %do;
  proc print;
    title "data _xxdata_ just after count and denom data have been merged";
  run;
%end;

%if %length(&gencode) > 0 %then %do;
  data _null_;
    file gencode mod;
    put "*************************************************************************************;";
    put "* Count the number of subjects in each population subgroup to use as denominators   *;";
    put "*                                                                                   *;";
    put "* Dataset _DENOM1 is created which contains a variable _NDENOM_ that has the values *;";
    put "* of the denominators                                                               *;";
    put "*************************************************************************************;";
    put "proc sort data=_xxdata_ nodupkey out=_den1_;";
    put "  by " @;
    %do i=1 %to &colcnt;
      put "&&col&i " @;
    %end; 
    put "&subj;";
    put "proc summary noprint;";
    put "  var &subj;";
    put "  by " @;
    %do i=1 %to &colcnt;
      put "&&col&i " @;
    %end;
    put ";";
    put "  output out=_denom1 n=_ndenom_;";
    put "run;";
    put;
    put "*******************************************************;";
    put "* Add in the denominator info to the analysis dataset *;";
    put "*******************************************************;";
    put "proc sort data=_xxdata_;";
    put "  by " @;
    %do i=1 %to &colcnt;
      put "&&col&i " @;
    %end;
    put ";";
    put "run;";
    put;
    %if &colorder=FORMATTED %then %do;
      %do i=1 %to &colcnt;
        %if &&colfmt&i ne %then %do;
          put "*** Variable CLTMPX&i is created to represent the formatted value of &&col&i, (format &&colfmt&i);";
        %end;
        %else %do;
          put "*** Variable CLTMPX&i is created to represent the value of &&col&i - variable is not formatted;";
        %end;
      %end;
    %end;
    put "data _xxdata_;";
    %if &colorder=FORMATTED %then %do;
      %do i=1 %to &colcnt;
        put "  length cltmpx&i $100;";
      %end;
    %end;
    put "  merge _xxdata_ _denom1;";
    put "  by " @;
    %do i=1 %to &colcnt;
      put "&&col&i " @;
    %end;
    put ";";
    %if &colorder=FORMATTED %then %do;
      put "  * create temporary variables to use for the formatted value of the columns;";
      %do i=1 %to &colcnt;
        %if &&colfmt&i ne %then %do;
          put "  cltmpx&i = put(&&col&i,&&colfmt&i);";
        %end;
        %else %do;
          put "  cltmpx&i = &&col&i;";
        %end;
      %end;
    %end;
    put "  * remove observations where the event data is missing;";
    put "  if " @;
    %do i=1 %to &varcnt;
      %if &i > 1 %then %do;
        put "and " @;
      %end;
      %if &&vrty&i=1 %then %do;
        put "&&var&i = . " @;
      %end;
	%else %if &&vrty&i=2 %then %do;
        put "compress(&&var&i) = '' " @;
      %end;
    %end;
    put "then delete;";
    put "run;";
    put;
  run;
%end;

%if &colorder=FORMATTED %then %do;
  %do i=1 %to &colcnt;
    %let col&i=cltmpx&i;
  %end;
  proc sort data=_xxdata_;
    by %do i=1 %to &colcnt;
       &&col&i
     %end;;
  run;
%end;

%if &debug=Y %then %do;
  proc print;
    title "data _xxdata_ just before working out the kkcnum macro (used in headings)";
  run;
%end;

%if %upcase(&addn)=Y or &any=Y or &total=Y %then %do;
  data _xxdata_;
    set _xxdata_ end=last;
	by %do i=1 %to &colcnt;
       &&col&i
     %end;;
    retain tempcnt 1 _totden_ 0;
    if first.&&col&colcnt then do;
      if _ndenom_ = . then call symput("kkcnum"||compress(put(tempcnt,8.0)),"0");
      else call symput("kkcnum"||compress(put(tempcnt,8.0)),compress(put(_ndenom_,8.0)));  
      tempcnt+1;
      * if _ndenom_ ne . then _totden_ = _ndenom_ + _totden_;
      _totden_ = 
      %do i=1 %to &colcnt;
        _den&i %if &i ne &colcnt %then +;
      %end;;
    end;
    if last then call symput("totdenom",compress(put(_totden_,10.0)));
  run;

  %if %length(&gencode) > 0 %then %do;
    data _null_;
      file gencode mod;
      put "data _xxdata_;";
      put "  set _xxdata_ end=last;";
	put "  by " @;
      %do i=1 %to &colcnt;
        put "&&col&i " @;
      %end;
      put ";";
      put "  retain _totden_ 0;";
      put "  if first.&&col&colcnt then do;";
      put "    if _ndenom_ ne . then _totden_ = _ndenom_ + _totden_;";
      put "  end;";
      put "  if last then call symput('totdenom',compress(put(_totden_,10.0)));";
      put "run;";
      put;
    run;
  %end;
%end;
%* get the right format for the number of events, patient-events and %s;
proc sort data=_xxdata_;
  by &by %do i=1 %to &colcnt;
           &&col&i
         %end; %if &any ne Y %then &var1; &subj;
run;

data _xxfmt_;
  set _xxdata_ end=last;
  by &by %do i=1 %to &colcnt;
           &&col&i
         %end; %if &any ne Y %then &var1; &subj;
  if first.&subj then _pe_ = 1;
  _ev_=1;
proc summary noprint;
  var _ev_ _pe_;
  by &by %do i=1 %to &colcnt;
       &&col&i
     %end; %if &any ne Y %then &var1;;
  output out=_xxfmt2_ sum=_nev_ _npe_;
proc summary noprint;
  var _nev_ _npe_;
  output out=_xxfmt3_ max=_maxev_ _maxpe_;
*proc print;
*  title "data _xxfmt3";
run;

data _null_;
  set _xxfmt3_;
  _max1_ = 1;
  _max2_ = 1;
  if _maxev_ >9 then _max1_ = 2;
  if _maxev_ > 99 then _max1_ = 3;
  if _maxev_ > 999 then _max1_ = 4;
  if _maxev_ > 9999 then _max1_ = 5;
  if _maxev_ > 99999 then _max1_ = 6;
  if _maxev_ > 999999 then _max1_ = 7;
  if _maxev_ > 9999999 then _max1_ = 8;
  if _maxpe_ >9 then _max2_ = 2;
  if _maxpe_ > 99 then _max2_ = 3;
  if _maxpe_ > 999 then _max2_ = 4;
  if _maxpe_ > 9999 then _max2_ = 5;
  if _maxpe_ > 99999 then _max2_ = 6;
  if _maxpe_ > 999999 then _max2_ = 7;
  if _maxpe_ > 9999999 then _max2_ = 8;
  %if &evfmt= %then %do;
    call symput("evfmt",put(_max1_,1.0)||".0");
  %end;
  %if &pefmt= %then %do;
    call symput("pefmt",put(_max2_,1.0)||".0");
  %end;
  %if &type=BOTH %then call symput("colw",13+_max1_+_max2_);    
  %else %if &type=PATIENT %then call symput("colw",9+_max2_);
  %else %if &type=EVENT %then call symput("colw",_max1_);;
run;

%* get the right format for the total column;
%if &total=Y %then %do;
proc sort data=_xxdata_;
  by &by %if &any ne Y %then &var1; &subj;
run;

data _xxfmt_;
  set _xxdata_ end=last;
  by &by %if &any ne Y %then &var1; &subj;
  if first.&subj then _pe_ = 1;
  _ev_=1;
proc summary noprint;
  var _ev_ _pe_;
  by &by %if &any ne Y %then &var1;;
  output out=_xxfmt2_ sum=_nev_ _npe_;
proc summary noprint;
  var _nev_ _npe_;
  output out=_xxfmt3_ max=_maxev_ _maxpe_;
*proc print;
*  title "data _xxfmt3";
run;

data _null_;
  set _xxfmt3_;
  _max1_ = 1;
  _max2_ = 1;
  if _maxev_ >9 then _max1_ = 2;
  if _maxev_ > 99 then _max1_ = 3;
  if _maxev_ > 999 then _max1_ = 4;
  if _maxev_ > 9999 then _max1_ = 5;
  if _maxev_ > 99999 then _max1_ = 6;
  if _maxev_ > 999999 then _max1_ = 7;
  if _maxev_ > 9999999 then _max1_ = 8;
  if _maxpe_ >9 then _max2_ = 2;
  if _maxpe_ > 99 then _max2_ = 3;
  if _maxpe_ > 999 then _max2_ = 4;
  if _maxpe_ > 9999 then _max2_ = 5;
  if _maxpe_ > 99999 then _max2_ = 6;
  if _maxpe_ > 999999 then _max2_ = 7;
  if _maxpe_ > 9999999 then _max2_ = 8;
  call symput("totevfmt",put(_max1_,1.0)||".0");
  call symput("totpefmt",put(_max2_,1.0)||".0");
run;
%end;

proc sort data=_xxdata_;
  by &by &var &subj;
run;

%if &debug=Y %then %do;
  proc print;
    var &by &var &subj cltmpx1;
    title "data sorted, ready to count";
  run;
%end;

%******************************************************************************;
%*** count the number of events                                             ***;
%*** dlen contains the length of the descriptor column                      ***;
%*** _c&i._&j contains the event-counts for the ith var in the jth column   ***;
%*** _p&i._&j contains the patient-counts for the ith var in the jth column ***;
%******************************************************************************;
%let dlen = 0;
%do i=1 %to &varcnt;
  proc sort data=_xxdata_;
    by &by &&var&i &subj;
  run;

  data _cnt&i;
    length _desc_ $ 200;
    set _xxdata_ end=last;
    by &by &&var&i &subj;
    retain _dlen_  
    %if &order=FREQ or &total=Y %then _tot _ptot;
    %do j=1 %to &numcols;
      _cnt&j _pcnt&j
    %end;;
    if _n_ = 1 then do;
      _dlen_ = 0;
    end;
    if first.&&var&i then do;
      _tot = .;
      _ptot = .;
      %do j=1 %to &numcols;
        _cnt&j=0;
        _pcnt&j=0;
      %end;
    end;
    %do j=1 %to &numcols;
      if %do k=1 %to &colcnt;
           &&col&k = %if &colorder=FORMATTED %then "&&cl&k._&j";
                     %else %if &&coltp&k=1 %then &&cv&k._&j;
                     %else %if "&&cl&k._&j"="Missing" %then "";
                     %else "&&cv&k._&j"; 
           %if &k ne &colcnt %then and;
         %end; 
      then do;
        _cnt&j=_cnt&j+1;
        if first.&subj then _pcnt&j=_pcnt&j+1;
      end;
    %end;
    if last.&&var&i then do;
      _level_ = &i;
      %if &i=1 %then %do;
        %if &&vrft&i = %then %do;
          _desc_ = %if &case=UPPER %then upcase(&&var&i);
                   %else %if &case=LOWER %then lowcase(&&var&i);
                   %else %if &case=UPPER1 %then upcase(substr(&&var&i,1,1))||lowcase(substr(&&var&i,2,length(&&var&i)-1));
                   %else &&var&i;;
        %end;
        %else %do;
          _desc_ = %if &case=UPPER %then upcase(put(&&var&i,&&vrft&i));
                   %else %if &case=LOWER %then lowcase(put(&&var&i,&&vrft&i));
                   %else %if &case=UPPER1 %then upcase(substr(put(&&var&i,&&vrft&i),1,1))||lowcase(substr(put(&&var&i,&&vrft&i),2,length(put(&&var&i,&&vrft&i))-1));
                   %else put(&&var&i,&&vrft&i);;
        %end;
      %end;
      %else %do;
        %if &&vrft&i = %then %do;
          _desc_=repeat(" ",%eval((&i-1)*2-1))||%if &case=UPPER %then upcase(&&var&i);
                                                %else %if &case=LOWER %then lowcase(&&var&i);
                                                %else %if &case=UPPER1 %then upcase(substr(&&var&i,1,1))||lowcase(substr(&&var&i,2,length(&&var&i)-1));
                                                %else &&var&i;;
        %end;
        %else %do;
          _desc_=repeat(" ",%eval((&i-1)*2-1))||%if &case=UPPER %then upcase(put(&&var&i,&&vrft&i));
                                                %else %if &case=LOWER %then lowcase(put(&&var&i,&&vrft&i));
                                                %else %if &case=UPPER1 %then upcase(substr(put(&&var&i,&&vrft&i),1,1))||lowcase(substr(put(&&var&i,&&vrft&i),2,length(put(&&var&i,&&vrft&i))-1));
                                                %else put(&&var&i,&&vrft&i);;
        %end;
      %end;
      if length(trim(_desc_))>_dlen_ then _dlen_ = length(trim(_desc_));
      %if &type=BOTH %then %do;
        %if &order=FREQ or &total=Y %then %do;
          _ptot=%do j=1 %to &numcols;
                  %if &j ne 1 %then +; _pcnt&j 
                %end;;
          _tot=%do j=1 %to &numcols;
                 %if &j ne 1 %then +; _cnt&j 
               %end;;
        %end;
        %do j=1 %to &numcols;
          col&j=put(_pcnt&j,&pefmt)||' ('||put(_pcnt&j/_den&j*100,&pctfmt)||'%) '||put(_cnt&j,&evfmt);
        %end;
        %if %upcase(&total)=Y %then %do;
          totcol=put(_ptot,&totpefmt)||' ('||put(_ptot/&totdenom*100,&pctfmt)||'%) '||put(_tot,&totevfmt);
        %end;
      %end;
      %else %if &type=PATIENT %then %do;
        %if &order=FREQ or &total=Y %then %do;
          _ptot=%do j=1 %to &numcols;
                  %if &j ne 1 %then +; _pcnt&j 
                %end;;
        %end;
        %do j=1 %to &numcols;
          col&j=put(_pcnt&j,&pefmt)||' ('||put(_pcnt&j/_den&j*100,&pctfmt)||'%)';
        %end;
        %if %upcase(&total)=Y %then %do;
          totcol=put(_ptot,&totpefmt)||' ('||put(_ptot/&totdenom*100,&pctfmt)||'%)';
        %end;
      %end;
      %else %if &type=EVENT %then %do;
        %if &order=FREQ %then %do;
          _tot=%do j=1 %to &numcols;
                 %if &j ne 1 %then +; _cnt&j 
               %end;;
        %end;
        %do j=1 %to &numcols;
          col&j=put(_cnt&j,&evfmt);
        %end;
        %if %upcase(&total)=Y %then %do;
          totcol=put(_tot,&totevfmt);
        %end;
      %end;
      output;
    end;
    if last and _dlen_ > &dlen then call symput("dlen",_dlen_);
  run;

  %if &debug = Y %then %do;
    proc print;
      title "data _cnt&i : counts at level &i : &&var&i";
    run;
  %end;
%end;

data _xxdata2;
  set %do i=1 %to &varcnt;
        _cnt&i
      %end;;
run;

%if &debug = Y %then %do; 
  proc print;
    title "data _xxdata2 : after events have been counted and levels merged";
  run;
%end;

%if %length(&gencode) > 0 %then %do;
  data _null_;
    file gencode mod;
    put "********************************************************;";
    put "* Sort the data prior to counting the number of events *;";
    put "********************************************************;";
    put "proc sort data=_xxdata_;";
    put "  by &by &var &subj;";
    put "run;";
    put;
    put "******************************************************************************;";
    put "* Count the number of events in each column subgroup and overall if required *;";
    put "******************************************************************************;";
    %do i=1 %to &varcnt;
      %if &order=FREQ or &total=Y %then %do;
        put "*** Variable _tot&i is created to represent the total events across subgroups for &&var&i ;";
      %end;
      %do j=1 %to &numcols;
        put "*** Variable _c&i._&j is created to represent the event count for " @;
        %do k=1 %to &colcnt;
          put "&&col&k = " @;
          %if &&coltp&k=1 %then %do;
            put "&&cl&k._&j " @;
          %end;
          %else %if "&&cl&k._&j"="Missing" %then %do;
            put "'' " @;
          %end;
          %else %do;
            put '"' @;
            put "&&cl&k._&j" @;
            put '" ' @; 
          %end;
          %if &k ne &colcnt %then %do;
            put "and " @;
          %end;
        %end;
        put ';';
        put "*** Variable _p&i._&j is created to represent the patient-event count for " @;
        %do k=1 %to &colcnt;
          put "&&col&k = " @;
          %if &&coltp&k=1 %then %do;
            put "&&cl&k._&j " @;
          %end;
          %else %if "&&cl&k._&j"="Missing" %then %do;
            put "'' " @;
          %end;
          %else %do;
            put '"' @;
            put "&&cl&k._&j" @;
            put '" ' @; 
          %end;
          %if &k ne &colcnt %then %do;
            put "and " @;
          %end;
        %end;
        put ';';
      %end;
    %end;
    put "*** Variable _level_ is created to store the level of event variable is being processed;";
    put "*** Variable _desc_ is created to store the description column that will be used in Proc Report;";
    %do j=1 %to &numcols;
      put "*** Variable col&j has been created to store the event count columns that will be used in Proc Report;";
    %end;
    %if %upcase(&total)=Y %then %do;
      put "*** Variable totcol has been created to store the total column that will be used on Proc Report;";
    %end;
    put "data _xxdata2;";
    put "  length _desc_ $ 200;";
    put "  set _xxdata_ end=last;";
    put "  by &by &var &subj;";
    put "  retain " @;
    %do i=1 %to &varcnt;
      %if &order=FREQ or &total=Y %then %do;
        put "_tot&i " @;
      %end;
      %do j=1 %to &numcols;
        put "_c&i._&j _p&i._&j " @;
      %end;
    %end;
    put ";";
/*
    put "  if _n_ = 1 then do;";
    %do i=1 %to &numcols;
      put "    any&i=0;";
    %end;
    put "  end;";
*/
    %do i=1 %to &varcnt;
      put "  if first.&&var&i then do;";
      %do j=1 %to &varcnt;
        put "    _tot&j=.;";
      %end;
      %do j=1 %to &numcols;
        put "    _c&i._&j=0;";
        put "    _p&i._&j=0;";
      %end;
      put "  end;";
      %do j=1 %to &numcols;
        put "  if " @;
        %do k=1 %to &colcnt;
          put "&&col&k = " @;
          %if &&coltp&k=1 %then %do;
            put "&&cl&k._&j " @;
          %end;
          %else %if "&&cl&k._&j"="Missing" %then %do;
            put "'' " @;
          %end;
          %else %do;
            put '"' @;
            put "&&cl&k._&j" @;
            put '" ' @; 
          %end;
          %if &k ne &colcnt %then %do;
            put "and " @;
          %end;
        %end;
        put "then _c&i._&j=_c&i._&j+1;";
        put "  if first.&subj and " @;
        %do k=1 %to &colcnt;
          put "&&col&k = " @;
          %if &&coltp&k=1 %then %do;
            put "&&cl&k._&j" @;
          %end;
          %else %if "&&cl&k._&j"="Missing" %then %do;
            put '"" ' @;
          %end;
          %else %do;
            put '"' @;
            put "&&cl&k._&j" @;
            put '" ' @; 
          %end;
          %if &k ne &colcnt %then %do;
            put "and " @;
          %end;
        %end;
        put "then _p&i._&j=_p&i._&j+1;";
      %end;
      put "  if last.&&var&i then do;";
      put "    _level_ = &i;";
      %if &i=1 %then %do;
        put "    _desc_ = " @;
        %if &case=UPPER %then %do;
          put "upcase(&&var&i);";
        %end;
        %else %if &case=LOWER %then %do;
          put "lowcase(&&var&i);";
        %end;
        %else %if &case=UPPER1 %then %do;
          put "upcase(substr(&&var&i,1,1))||lowcase(substr(&&var&i,2,length(&&var&i)-1));";
        %end;
        %else %do;
          put "&&var&i;";
        %end;
      %end;
      %else %do;
        put "    _desc_=repeat(" @;
        put '" ' @;
        put '",' @;
        put "%eval((&i-1)*2-1))||" @;
        %if &case=UPPER %then  %do;
          put "upcase(&&var&i);";
        %end;
        %else %if &case=LOWER %then  %do;
          put "lowcase(&&var&i);";
        %end;
        %else %if &case=UPPER1 %then  %do;
          put "upcase(substr(&&var&i,1,1))||lowcase(substr(&&var&i,2,length(&&var&i)-1));";
        %end;
        %else  %do;
          put "&&var&i;";
        %end;
      %end;
      %do j=1 %to &numcols;
        put "    _cnt&j=_c&i._&j;";
        put "    _pcnt&j=_p&i._&j;";
      %end;
      %if &type=BOTH %then %do;
        %if &order=FREQ or &total=Y %then %do;
          put "    _ptot&i=" @;
          %do j=1 %to &numcols;
            %if &j ne 1 %then %do;
              put "+" @;
            %end;
            put "_pcnt&j " @; 
          %end;
          put ";";
          put "    _tot&i=" @;
          %do j=1 %to &numcols;
            %if &j ne 1 %then %do;
              put "+" @;
            %end;
            put "_cnt&j " @;
          %end;
          put ";";
        %end;
        %do j=1 %to &numcols;
          put "    col&j=put(_pcnt&j,&pefmt)||' ('||put(_pcnt&j/_den&j*100,&pctfmt)||'%) '||put(_cnt&j,&evfmt);";
        %end;
        %if %upcase(&total)=Y %then %do;
          put "    totcol=put(_ptot&i,&totpefmt)||' ('||put(_ptot&i/&totdenom*100,&pctfmt)||'%) '||put(_tot&i,&totevfmt);";
        %end;
      %end;
      %else %if &type=PATIENT %then %do;
        %if &order=FREQ or &total=Y %then %do;
          put "    _ptot&i=" @;
          %do j=1 %to &numcols;
            %if &j ne 1 %then %do;
              put "+" @;
            %end;
            put "_pcnt&j " @; 
          %end;
          put ";";
        %end;
        %do j=1 %to &numcols;
          put "    col&j=put(_pcnt&j,&pefmt)||' ('||put(_pcnt&j/_den&j*100,&pctfmt)||'%)';";
        %end;
        %if %upcase(&total)=Y %then %do;
          put "    totcol=put(_ptot&i,&totpefmt)||' ('||put(_ptot&i/&totdenom*100,&pctfmt)||'%)';";
        %end;
      %end;
      %else %if &type=EVENT %then %do;
        %if &order=FREQ %then %do;
          put "    _tot&i=" @;
          %do j=1 %to &numcols;
            %if &j ne 1 %then %do;
              put "+";
            %end;
            put "_cnt&j " @;
          %end;
          put ";";
        %end;
        %do j=1 %to &numcols;
          put "    col&j=put(_cnt&j,&evfmt);";
        %end;
        %if %upcase(&total)=Y %then %do;
          put "    totcol=put(_tot&i,&totevfmt);";
        %end;
      %end;
      put "    output;";
      put "  end;";
    %end;
    put "run;";
    put;
  run;
%end;

%if &order = FREQ %then %do;
  proc sort;
    by %do i=1 %to &bycnt;
                     &&by&i
                   %end; &var1 _level_
       %do i=2 %to &varcnt;
         &&var&i
       %end;;
  run;

  data _xxdata2;
    set _xxdata2;
    by %do i=1 %to &bycnt;
                     &&by&i
                   %end; &var1 _level_
       %do i=2 %to &varcnt;
         &&var&i
       %end;;
    retain %do i=1 %to &varcnt;
             ord&i
		   %end;;
    %do i=1 %to &varcnt;
      if first.&&var&i then ord&i = %if &type=EVENT %then _tot;%else _ptot;;
    %end;
  proc sort;
    by &by   
       %if &order=FREQ %then %do;
          descending ord1
       %end;
       &var1  _level_ 
       %if &varcnt>1 %then %do;
         %do i=2 %to &varcnt;
	       %if &order=FREQ %then descending ord&i  ; &&var&i 
         %end;
       %end;;
  run;

  %if %length(&gencode) > 0 %then %do;
    data _null_;
      file gencode mod;
      put "* create a variable that will allow the correct ordering;";
      put "proc sort;";
      put "  by " @;
      %do i=1 %to &bycnt;
        put "&&by&i " @;
      %end;
      put "&var1 _level_ " @;
      %do i=2 %to &varcnt;
        put "&&var&i " @;
      %end;
      put ";";
      put "run;";
      put ;
      put "data _xxdata2;";
      put "  set _xxdata2;";
      put "  by " @;
      %do i=1 %to &bycnt;
        put "&&by&i " @;
      %end;
      put "&var1 _level_ " @;
      %do i=2 %to &varcnt;
        put "&&var&i " @;
      %end;
      put ";";
      put "  retain " @;
      %do i=1 %to &varcnt;
        put "ord&i " @;
	%end;
      put ";";
      %do i=1 %to &varcnt;
        put "  if first.&&var&i then ord&i = _tot&i;";
      %end;
      put "proc sort;";
      put "  by &by " @;
      %if &order=FREQ %then %do;
        put "descending ord1 " @;
      %end;
      put "&var1  _level_ " @;
      %if &varcnt>1 %then %do;
        %do i=2 %to &varcnt;
          %if &order=FREQ %then %do;
            put "descending ord&i " @;
          %end;
          put "&&var&i " @; 
        %end;
      %end;
      put ";";
      put "run;";
      put;
    run;
  %end;
%end;
%else %do;
  proc sort;
    by %do i=1 %to &bycnt;
                     &&by&i
                   %end; &var1 _level_
       %do i=2 %to &varcnt;
         &&var&i
       %end;;
  run;

  %if %length(&gencode) > 0 %then %do;
    data _null_;
      file gencode mod;
      put "proc sort;";
      put "  by " @;
      %do i=1 %to &bycnt;
        put "&&by&i " @;
      %end;
      put "&var1 _level_ " @;
      %do i=2 %to &varcnt;
        put "&&var&i " @;
      %end;
      put ";";
      put "run;";
      put;
    run;
  %end;
%end;

%if &any=Y %then %do;
  proc sort data=_xxdata_;
    by &by &subj;
  run;

  data _xxany_;
    set _xxdata_ end=last;
    by &by &subj;
    retain %do i=1 %to &numcols;
             _any&i _pany&i
           %end; 0 ;
	%do j=1 %to &numcols;
      if %do k=1 %to &colcnt;
           &&col&k = %if &colorder=FORMATTED %then "&&cl&k._&j";
                     %else %if &&coltp&k=1 %then &&cv&k._&j;
                     %else %if "&&cl&k._&j"="Missing" %then "";
                     %else "&&cv&k._&j";
           %if &k ne &colcnt %then and ;
         %end; then _any&j=_any&j+1;
      if first.&subj and %do k=1 %to &colcnt;
                           &&col&k = %if &colorder=FORMATTED %then "&&cl&k._&j";
                                     %else %if &&coltp&k=1 %then &&cv&k._&j;
                                     %else %if "&&cl&k._&j"="Missing" %then "";
                                     %else "&&cv&k._&j";
                           %if &k ne &colcnt %then and ;
                         %end; then _pany&j = _pany&j + 1;
	%end;
	if last then do;
	  _level_ = 1;
      _desc_ = %if &case=UPPER %then "%upcase(&anydesc)";
	           %else %if &case=LOWER %then "%lowcase(&anydesc)";
	           %else %if &case=UPPER1 %then "%upcase(%substr(&anydesc,1,1))%lowcase(%substr(&anydesc,2,%eval(%length(&anydesc)-1)))";
		       %else "&anydesc";;
	  if length(trim(_desc_))>&dlen then do;
        _dlen_ = length(trim(_desc_));
		call symput("dlen",_dlen_);
	  end;
      %if &total=Y %then %do;
        _ptot_ = 
        %do j=1 %to &numcols;
          _pany&j %if &j ne &numcols %then +;
        %end;;
        _tot_ = 
        %do j=1 %to &numcols;
          _any&j %if &j ne &numcols %then +;
        %end;;
      %end;
	  %if &type=BOTH %then %do;
	    %if &order=FREQ %then ord1=99999;;
  	    %do j=1 %to &numcols;
          col&j=put(_pany&j,&pefmt)||' ('||put(_pany&j/_den&j*100,&pctfmt)||'%) '||put(_any&j,&evfmt);
		%end;
        %if &total=Y %then %do;
          totcol=put(_ptot_,&totpefmt)||' ('||put(_ptot_/_totden_*100,&pctfmt)||'%) '||put(_tot_,&totevfmt);
        %end;
	  %end;
	  %else %if &type=PATIENT %then %do;
	    %if &order=FREQ %then ord1=99999;;
	    %do j=1 %to &numcols;
          col&j=put(_pany&j,&pefmt)||' ('||put(_pany&j/_den&j*100,&pctfmt)||'%)';
		%end;
        %if &total=Y %then %do;
          totcol=put(_ptot_,&totpefmt)||' ('||put(_ptot_/_totden_*100,&pctfmt)||'%)';
        %end;
	  %end;
	  %else %if &type=EVENT %then %do;
	    %if &order=FREQ %then ord1=99999;;
	    %do j=1 %to &numcols;
          col&j=put(_any&j,&evfmt);
		%end;
        %if &total=Y %then %do;
          totcol=put(_tot_,&totevfmt);
        %end;
	  %end;
	  %do i=1 %to &varcnt;
	    %if &&vrty&i = 1 %then &&var&i = .;
		%else &&var&i = '';;
	  %end;
      output;
	end;
  run;

  data _xxdata2;
    set _xxdata2 _xxany_ (in=inany);
	if inany then anyflag=1;
	else anyflag=1;
  run;

  %if &debug = Y %then %do;
    proc print;
      title "Data after the any event counts have been added";
  %end;

  %if %length(&gencode) > 0 %then %do;
    data _null_;
      file gencode mod;
      put "proc sort data=_xxdata_;";
      put "  by &by &subj;";
      put "run;";
      put;
      put "*********************************;";
      put "* Work out the ANY EVENT counts *;";
      put "*********************************;";
      %do i=1 %to &numcols;
        put "*** Variable _any&i is created to store the ANY EVENT counts for column &i;";
        put "*** Variable _pany&i is created to store the ANY EVENT patient-counts for column &i;";
      %end;
      put "data _xxany_;";
      put "  set _xxdata_ end=last;";
      put "  by &by &subj;";
      put "  retain " @;
      %do i=1 %to &numcols;
        put "_any&i _pany&i " @;
      %end;
      put "0 ;";
      %do j=1 %to &numcols;
        put "  if " @;
        %do k=1 %to &colcnt;
          put "&&col&k = " @;
          %if &&coltp&k=1 %then %do;
            put "&&cl&k._&j " @;
          %end;
          %else %if "&&cv&k._&j"="Missing" %then %do;
            put '"" '@;
          %end;
          %else  %do;
            put '"' @;
            put "&&cv&k._&j" @;
            put '" ' @;
          %end;
          %if &k ne &colcnt %then %do;
            put "and " @;
          %end;
        %end;
        put " then _any&j=_any&j+1;";
        put "  if first.&subj and " @;
        %do k=1 %to &colcnt;
          put "&&col&k = " @;
          %if &&coltp&k=1 %then %do;
            put "&&cv&k._&j " @;
          %end;
          %else %if "&&cv&k._&j"="Missing" %then %do;
            put '"" ' @;
          %end;
          %else %do;
            put '"' @;
            put "&&cv&k._&j" @;
            put '" ' @;
          %end;
          %if &k ne &colcnt %then %do;
            put "and " @;
          %end;
        %end;
        put " then _pany&j = _pany&j + 1;";
      %end;
	put "  if last then do;";
	put "    _level_ = 1;";
      put "    _desc_ = " @;
      %if &case=UPPER %then %do;
        put '"' @;
        put "%upcase(&anydesc)" @;
        put '";';
      %end;
	%else %if &case=LOWER %then %do;
        put '"' @;
        put "%lowcase(&anydesc)" @;
        put '";';
      %end;
	%else %if &case=UPPER1 %then %do;
        put '"' @;
        put "%upcase(%substr(&anydesc,1,1))%lowcase(%substr(&anydesc,2,%eval(%length(&anydesc)-1)))" @;
        put '";';
      %end;
      %else %do;
        put "&anydesc;";
      %end;
      %if &total=Y %then %do;
        put "    _ptot_ = " @;
        %do j=1 %to &numcols;
          put "_pany&j " @;
          %if &j ne &numcols %then %do;
            put "+" @;
          %end;
        %end;
        put ";";
        put "    _tot_ = " @;
        %do j=1 %to &numcols;
          put "_any&j " @;
          %if &j ne &numcols %then %do;
            put "+" @;
          %end;
        %end;
        put ";";
      %end;
      %if &type=BOTH %then %do;
        %if &order=FREQ %then %do;
          put "    ord1=99999;";
        %end;
  	  %do j=1 %to &numcols;
          put "    col&j=put(_pany&j,&pefmt)||' ('||put(_pany&j/_den&j*100,&pctfmt)||'%) '||put(_any&j,&evfmt);";
        %end;
        %if &total=Y %then %do;
          put "    totcol=put(_ptot_,&totpefmt)||' ('||put(_ptot_/_totden_*100,&pctfmt)||'%) '||put(_tot_,&totevfmt);";
        %end;
      %end;
      %else %if &type=PATIENT %then %do;
        %if &order=FREQ %then %do;
          put "    ord1=99999;";
        %end;
        %do j=1 %to &numcols;
          put "    col&j=put(_pany&j,&pefmt)||' ('||put(_pany&j/_den&j*100,&pctfmt)||'%)';";
        %end;
        %if &total=Y %then %do;
          put "    totcol=put(_ptot_,&totpefmt)||' ('||put(_ptot_/_totden_*100,&pctfmt)||'%)';";
        %end;
      %end;
      %else %if &type=EVENT %then %do;
        %if &order=FREQ %then %do;
          put "    ord1=99999;";
        %end;
        %do j=1 %to &numcols;
          put "    col&j=put(_any&j,&evfmt);";
        %end;
        %if &total=Y %then %do;
          put "    totcol=put(_tot_,&totevfmt);";
        %end;
      %end;
      %do i=1 %to &varcnt;
        %if &&vrty&i = 1 %then %do;
          put "    &&var&i = .;"; 
        %end;
        %else %do;
          put "    &&var&i = '';";
        %end;
      %end;
      put "    output;";
      put "  end;";
      put "run;";
      put;
      put "*** Variable anyflag is a flag that indicates whether the record contains the ANY EVENT data;";
      put "data _xxdata2;";
      put "  set _xxdata2 _xxany_ (in=inany);";
	put "  if inany then anyflag=1;";
	put "  else anyflag=1;";
      put "run;";
      put;
    run;
  %end;
%end;

%if %upcase(&addn)=Y and "&by"="" %then %do;
  %do i=1 %to &numcols;
    %let cl&colcnt._&i=&&cl&colcnt._&i.&split(N=&&kkcnum&i);
  %end;
  %if &total=Y %then %let tothead=&tothead.&split.(N=&totdenom);
%end;

%let flowflag=0;
%if "&eventlbl"="" %then %do;
  %if &flow ne %then %let flowflag=1;
  %let space = %str(   );
  %do i=1 %to &varcnt;
	%put setting event label to &&vrlb&i;
    %if &i=1 %then %let eventlbl=&&vrlb&i;
    %else %do;
      %let eventlbl=&eventlbl&split&space&&vrlb&i;
  	  %put setting event label to &eventlbl&split&space&&vrlb&i;
	  %let space = &space&space;
	%end;
  %end;
%end;
%put the event label is &eventlbl;

%if &flow ne %then %do;
  data _xxdata2;
    length _xtmp_ $200 _flw_ $ &flow;
    set _xxdata2;
	_xlevx_=1;
    _xcnt_=1;
    _xtmp_=scan(_desc_,_xcnt_," ");
    do while (_xtmp_ ne '');
      %** fill up the current line ***;
      _xexitx_=0;
      _flw_='';
      do while(_xexitx_=0);
		if _flw_ ne '' then _xspc1=&flow-1-length(trim(_flw_))-(_level_-1)*2;
        else _xspc1 = &flow-(_level_-1)*2;
		*put "POINT 1: _flw_ = " _flw_ " : _xtmp_ = " _xtmp_ " : _xspc1 = " _xspc1 " : desc = " _desc_ " level = " _level_ ;
        if _xtmp_ ne '' and length(trim(_xtmp_))<=_xspc1 then do;
          if _flw_ ne '' then _flw_=trim(_flw_)||' '||_xtmp_;
          else if _level_=1 then _flw_ = _xtmp_;
		  else _flw_ = repeat(" ",(_level_-1)*2-1)||_xtmp_;
          if _flw_ ne '' then _xspc1=&flow-1-length(trim(_flw_))-(_level_-1)*2;
          else _xspc1 = &flow-(_level_-1)*2;
          _xcnt_=_xcnt_+1;
          _xtmp_=scan(_desc_,_xcnt_," ");
        end;
        else _xexitx_=1;
      end;
      if length(_xtmp_)>(&flow-(_level_-1)*2-1) and _flw_ = '' then do;
*        _flw_=repeat(" ",%eval((&i-1)*2-1))||substr(_xtmp_,1,&flow)||'-';
*        _xtmp_=substr(_xtmp_,&flow,length(_xtmp_)-&flow);
  	    *put "POINT 2: _flw_ = " _flw_ " :flow=&flow: _level_="_level_":_xtmp_="_xtmp_;
        if _level_=1 then _flw_=substr(_xtmp_,1,&flow-1)||'-';
		else _flw_ = repeat(" ",(_level_-1)*2-1)||substr(_xtmp_,1,&flow-(_level_-1)*2-1)||'-';
  	    *put "POINT 2B: _flw_ = " _flw_ " :flow=&flow: _level_="_level_":_xtmp_="_xtmp_;
        *_xtmp_=substr(_xtmp_,&flow-(_level_-1)*2,length(_xtmp_)-&flow-(_level_-1)*2-3);
        _xtmp_=substr(_xtmp_,&flow-(_level_-1)*2);
  	    *put "POINT 3: _flw_ = " _flw_ " :flow=&flow: _level_="_level_":_xtmp_="_xtmp_;
		*put "POINT 4: _flw_ = " _flw_ " : _xtmp_ = " _xtmp_ " : _xspc1 = " _xspc1 " : desc = " _desc_ " level = " _level_;
      end;
      _orign_ = _n_;
	  *put "POINT 5: _flw_ = " _flw_ " : _xtmp_ = " _xtmp_ " : _xspc1 = " _xspc1 " : desc = " _desc_ " level = " _level_;
      output;
      _xlevx_=_xlevx_+1;
      %* set all the other variables missing*;
      if _xlevx_ = 2 then do;
        %do i=1 %to &numcols;
	      col&i='';
        %end;
        %if &total=Y %then %do;
          totcol='';
        %end;
      end;
    end;
	drop _desc_;
	rename _flw_ = _desc_;
  run;

  %if &debug = Y %then %do;
    proc print;
      title "data after the columns have been wrapped";
    run;
  %end;

  %if %length(&gencode) > 0 %then %do;
    data _null_;
      file gencode mod;
      put "****************************************************************************;";
      put "* Wrap the Event Descriptions within the column if the flow option is used *;"; 
      put "****************************************************************************;";
      put "*** Variable _flw_ is created to store the wrapped values of the event description;";
      put "data _xxdata2;";
      put "  length _xtmp_ $200 _flw_ $ &flow;";
      put "  set _xxdata2;";
	put "  _xlevx_=1;";
	put "  _xcnt_=1;";
	put "  _xtmp_=scan(_desc_,_xcnt_,' ');";
	put "  do while (_xtmp_ ne '');";
	put "    * fill up the current line ***;";
	put "    _xexitx_=0;";
	put "    _flw_='';";
	put "    do while(_xexitx_=0);";
	put "      if _flw_ ne '' then _xspc1=&flow-1-length(trim(_flw_))-(_level_-1)*2;";
	put "      else _xspc1 = &flow-(_level_-1)*2;";
	put "      if _xtmp_ ne '' and length(trim(_xtmp_))<=_xspc1 then do;";
	put "        if _flw_ ne '' then _flw_=trim(_flw_)||' '||_xtmp_;";
	put "        else if _level_=1 then _flw_ = _xtmp_;";
	put "	       else _flw_ = repeat(" @;
      put '" ' @;
      put '",(_level_-1)*2-1)||_xtmp_;';
	put "        if _flw_ ne '' then _xspc1=&flow-1-length(trim(_flw_))-(_level_-1)*2;";
	put "        else _xspc1 = &flow-(_level_-1)*2;";
	put "        _xcnt_=_xcnt_+1;";
	put "        _xtmp_=scan(_desc_,_xcnt_,' ');";
	put "      end;";
	put "      else _xexitx_=1;";
	put "    end;";
	put "    if length(_xtmp_)>(&flow-(_level_-1)*2-1) and _flw_ = '' then do;";
	put "      if _level_=1 then _flw_=substr(_xtmp_,1,&flow-1)||'-';";
	put "	     else _flw_ = repeat(" @;
      put '" ' @;
      put '",(_level_-1)*2-1)||substr(_xtmp_,1,' @;
      put "&flow-(_level_-1)*2-1)||'-';";
	put "      _xtmp_=substr(_xtmp_,&flow-(_level_-1)*2);";
	put "    end;";
	put "    _orign_ = _n_;";
	put "    output;";
	put "    _xlevx_=_xlevx_+1;";
	put "    * set all the other variables missing*;";
	put "    if _xlevx_ = 2 then do;";
      %do i=1 %to &numcols;
	  put "      col&i='';";
      %end;
      %if &total=Y %then %do;
        put "      totcol='';";
      %end;
      put "    end;";
      put "  end;";
	put "  drop _desc_;";
	put "  rename _flw_ = _desc_;";
      put "run;";
      put;
    run;
  %end;

  %if &dlen>&flow %then %let dlen=&flow;

  %if &flowflag %then %do;
  %*** wrap the title lines as well ***;
  %let lshead=0;
  %let space=%str(  );
  %let empty=;
  %let newhead =;
  %let current =;
    %let lscnt=1;
    %let bit = %scan(%bquote(&eventlbl),&lscnt,"&split");
	%put Selecting bit "&bit" for processing;
	%put BIT is &bit;
    %let bitlen = %length(&bit);
    %if %bquote(&bit)= %then %let varhead=0;
    %else %let varhead=1;
    %do %while(%bquote(&bit)~=);
      %if &bitlen>&flow %then %do;  
        %let wordcnt = 1;
        %let word = %scan(%bquote(&bit),&wordcnt," ");
		%*put START: the next word selected for processing is &word: wordcnt is &wordcnt;
        %do %while(%bquote(&word)~=);
		  %if %length(&word)<=%eval(&flow-(&lscnt-1)*2) and &current= %then %do;
		    %*put START OPTION 1 newhead=&newhead:current=&current:word=&word:bit=&bit-;
		    %let current=&word;
            %let wordcnt = %eval(&wordcnt+1);
            %let word = %scan(%bquote(&bit),&wordcnt," ");
		    %if &word= %then %if &newhead= %then %let newhead=&empty&current;
		    %else %if &word= %then %let newhead=&newhead&split&empty&current;
		    %*put END OPTION 1 newhead=&newhead:current=&current:word=&word:bit=&bit-;
		  %end;
		  %else %if %length(&word)>%eval(&flow-(&lscnt-1)*2) and &current= %then %do;
		    %*put START OPTION 2 newhead=&newhead:current=&current:word=&word:bit=&bit-;
		    %if &newhead= %then %let newhead=%substr(&word,1,%eval(&flow-1))%str(-);
		    %else %let newhead=&newhead&split&empty%substr(&word,1,%eval(&flow-(&lscnt-1)*2-1))%str(-);
		    %let lshead=%eval(&lshead+1);
			%*put the length of the word is %length(word);
			%*put the start point for the next line is %eval(&flow-(&lscnt-1)*2);
			%*put the space required for the blank spaces is %eval((&lscnt-1)*2);
			%*put the length of the string on the next line is %eval(%length(word)-&flow-(&lscnt-1)*2+1);
		    %let word=%substr(&word,%eval(&flow-(&lscnt-1)*2));
		    %*put END OPTION 2 newhead=&newhead:current=&current:word=&word:bit=&bit-;
		  %end;
          %else %if %length(&word)<=%eval(&flow-%length(&current)-(&lscnt-1)*2-1) %then %do;
		    %*put START OPTION 3 newhead=&newhead:current=&current:word=&word:bit=&bit-;
		    %let current=&current &word;
            %let wordcnt = %eval(&wordcnt+1);
            %let word = %scan(%bquote(&bit),&wordcnt," ");
			%if &newhead ne and &word= %then %let newhead=&newhead&split&empty&current;
			%else %if &word= %then %let newhead=&empty&current;
		    %*put END OPTION 3 newhead=&newhead:current=&current:word=&word:bit=&bit-;
		  %end;
          %else %if %length(&word)>%eval(&flow-%length(&current)-(&lscnt-1)*2-1) %then %do;
		    %*put START OPTION 4 newhead=&newhead:current=&current:word=&word:bit=&bit-;
		    %if &newhead= %then %let newhead=&current;
			%else %let newhead=&newhead&split&empty&current;
		    %let lshead=%eval(&lshead+1);
		    %let current=;
		    %*put END OPTION 4 newhead=&newhead:current=&current:word=&word:bit=&bit-;
		  %end;
		  %else %do;
		    %*put ERROR: newhead=&newhead:current=&current:word=&word:bit=&bit-;
		  %end;
		  %*put TEST1: newhead=&newhead:current=&current:word=&word:bit=&bit-;
		%end;
		%*put TEST2: newhead=&newhead:current=&current:word=&word:bit=&bit-;
	  %end;
	  %else %do;
	    %if &newhead= %then %let newhead=&bit;
	    %else %let newhead=&newhead&split&bit;
  	    %*put TEST3: newhead=&newhead:current=&current:word=&word:bit=&bit-;
	  %end;
      %let varhead=%eval(&varhead+1);
      %let bitlen=%length(&bit);
	  %*put TEST4: newhead=&newhead:current=&current:word=&word:bit=&bit-;
      %let lscnt=%eval(&lscnt+1);
   	  %let empty=&empty&space;
      %let bit = %scan(%bquote(&eventlbl),&lscnt,"&split");
      %let bitlen=%eval(&bitlen+%length(&bit)+1);
	  %let current=;
	  %*put TEST5: newhead=&newhead:current=&current:word=&word:bit=&bit-;
    %end;
    %*put TEST6: newhead=&newhead:current=&current:word=&word:bit=&bit;
    %*put the new heading is &newhead, over &lscnt lines;
	%let eventlbl=&newhead&split;
  %end;
%end;


%if "&lasthead" = "" %then %do;
  %if &type=BOTH %then %let lasthead=%nrbquote(n      %    #);
  %else %if &type=PATIENT %then %let lasthead=%nrbquote(n      %);
  %else %if &type=EVENT %then %let lasthead=%nrbquote(#);
%end;

%do i=1 %to &numcols;
  %let cl&colcnt._&i=&&cl&colcnt._&i.&split.&lasthead;
%end;
%if &total=Y %then %let tothead=&tothead.&split.&lasthead;

%* find the length of the column headings;
%do i=1 %to &numcols;
  %let clen&i=0;
  %let cnt=1;
  %let word = %scan(&&cl&colcnt._&i,&cnt,&split);
  %do %while("&word"~="");
    %if &&clen&i<%length(&word) %then %let clen&i=%length(&word);
    %let cnt=%eval(&cnt+1);
    %let word = %scan(&&cl&colcnt._&i,&cnt,&split);
  %end;
%end;
%if &total=Y %then %do;
  %let clentot=0;
  %let cnt=1;
  %let word = %scan(&tothead,&cnt,&split);
  %do %while("&word"~="");
    %if &clentot<%length(&word) %then %let clentot=%length(&word);
    %let cnt=%eval(&cnt+1);
    %let word = %scan(&tothead,&cnt,&split);
  %end;
%end;

%* check the width of the data in the columns;
data _null_;
  set _xxdata2 end=last;
  retain %do i=1 %to &numcols;
           wid&i
         %end; 
         %if &total=Y %then %do;
           widtot
         %end; 0;
  %do i=1 %to &numcols;
    if length(trim(col&i)) > wid&i then wid&i = length(trim(col&i));
  %end;
  %if &total=Y %then %do;
    if length(trim(totcol)) > widtot then widtot = length(trim(totcol));
  %end;
  if last then do;
    %do i=1 %to &numcols;
      call symput("cw&i",wid&i);
	%end;
    %if &total=Y %then %do;
      call symput("cwtot",widtot);
    %end;
  end;
run;

%do i=1 %to &numcols;
  %put the length of the data in column &i is &&cw&i, comparing with &&clen&i;
  %if &&clen&i < &&cw&i %then %let clen&i = &&cw&i;
%end;
%if &total=Y %then %do;
  %if &clentot < &cwtot %then %let clentot = &cwtot;
%end;
%* --------- add in the page n of n details if required ---------;
%if &pagenum=Y %then %do;

  %*** first, get the length of the page ***;
  data _null_;
    set sashelp.voption;
    if optname='LINESIZE' then call symput("linesize",setting);
    else if optname='PAGESIZE' then call symput("pagesize",setting);
  run;

  %*** then check the number of titles and footnotes ***;
  proc sort data=sashelp.vtitle out=kktitle1;
    by type number;
  run;

  %let numtitle=0;
  %let numfootn=0;
  data _null_;
    set kktitle1;
    by type number;
    if last.type then do;
      if type='T' then call symput("numtitle",number);
      else if type='F' then call symput("numfootn",number);
    end;
  run;

  %*** one extra line for the heading underline and the space below
       and one for the bottom underline and the page X of X ***;
  %let repextra=5;
  %if &numtitle>0 %then %let repextra=%eval(&repextra+1); %* an extra spacer line *;
  %if &numfootn>0 %then %let repextra=%eval(&repextra+1); %* an extra spacer line * ;

  %let repspace=%eval(&pagesize-&numtitle-&numfootn-&cnt-&repextra);                    
                    
  %put NOTE: Page Size=%trim(&pagesize) Report Space=%trim(&repspace) No. titles=%trim(&numtitle) No. footnotes=%trim(&numfootn) Header space=%trim(&lshead);

  %*** get the width of the report ***;
  %let totwidth=0;
  %do i=1 %to &numcols;
    %let totwidth=%eval(&totwidth+&&clen&i);
    %if &i ne &varcnt %then %let totwidth=%eval(&totwidth+&spacing);
  %end; 
  %if &total=Y %then %do;
    %let totwidth=%eval(&totwidth+&clentot+&spacing);
  %end;
  %let totwidth=%eval(&totwidth+&spacing+&dlen);

  %*put The report is going to be &totwidth characters wide;
  %*** get the start column of the report ***;
  %let startcol=0;
  %if &totwidth>&linesize %then %put NOTE: The Report is too wide.  Try using smaller labels or using the FLOW option;
  %else %let startcol=%eval((&linesize-&totwidth)/2+1);

  proc sort data=_xxdata2; 
    by &by 
         %if &any=Y %then anyflag;
         %if &order=FREQ %then descending ord1; &var1 _level_ 
         %if &varcnt>1 %then %do;
           %do i=2 %to &varcnt;
             %if &order=FREQ %then descending ord&i; &&var&i 
           %end;
         %end;
		 %if &flow ne %then _xlevx_;
         _desc_ %do i = 1 %to &numcols;
                    col&i
                %end;;
  run;

  data _xxdata2;
    set _xxdata2 end=last;
	by &by 
      %if &any=Y %then anyflag;
      %if &order=FREQ %then descending ord1; &var1 _level_ 
      %if &varcnt>1 %then %do;
        %do i=2 %to &varcnt;
           %if &order=FREQ %then descending ord&i; &&var&i 
        %end;
      %end;
	  %if &flow ne %then _xlevx_;
      _desc_ %do i = 1 %to &numcols;
                 col&i
             %end;;
    retain _lsline_ 0;
    %if &skip ne %then %do;
      if first.&&var&skip then _lsline_=_lsline_+1;
    %end;
    _lsline_= _lsline_+1;
    _lspage_=ceil(_lsline_/&repspace);
    if last then call symput("reppages",_lspage_);
  run;  

  %if %length(&gencode) > 0 %then %do;
    data _null_;
      file gencode mod;
      put "***************************************************************;";
      put "* Sort the data prior to adding paging details to the dataset *;";
      put "***************************************************************;";
      put "proc sort data=_xxdata2;";
      put "  by &by " @;
      %if &any=Y %then %do;
        put "anyflag " @;
      %end;
      %if &order=FREQ %then %do;
        put "descending ord1 " @;
      %end;
      put "&var1 _level_ " @;
      %if &varcnt>1 %then %do;
        %do i=2 %to &varcnt;
          %if &order=FREQ %then %do;
            put "descending ord&i " @;
          %end;
          put "&&var&i " @;
        %end;
      %end;
      %if &flow ne %then %do;
        put "_xlevx_ " @;
      %end;
      put "_desc_ " @;
      %do i = 1 %to &numcols;
        put "col&i " @;
      %end;
      put ";";
      put "run;";
      put;
      put "*************************************;";
      put "* Add paging details to the dataset *;";
      put "*************************************;";
      put "*** Variable _lsline_ stores the current line of the report;";
      put "*** Variable _lspage_ stores the current page of the report;";
      put "data _xxdata2;";
      put "  set _xxdata2 end=last;";
      put "  by &by " @;
      %if &any=Y %then %do;
        put "anyflag " @;
      %end;
      %if &order=FREQ %then %do;
        put "descending ord1 " @;
      %end;
      put "&var1 _level_ " @;
      %if &varcnt>1 %then %do;
        %do i=2 %to &varcnt;
          %if &order=FREQ %then %do;
            put "descending ord&i " @;
          %end;
          put "&&var&i " @;
        %end;
      %end;
      %if &flow ne %then %do;
        put "_xlevx_ " @;
      %end;
      put "_desc_ " @;
      %do i = 1 %to &numcols;
        put "col&i " @;
      %end;
      put ";";
      put "  retain _lsline_ 0;";
      %if &skip ne %then %do;
        put "  * add an extra line for the space created by the skip variable;";
        put "  if first.&&var&skip then _lsline_=_lsline_+1;";
      %end;
      put "  _lsline_= _lsline_+1;";
      put "  _lspage_=ceil(_lsline_/&repspace);";
      put "run;";  
      put;
    run;
  %end;

  %*** work out where to put the page X of X ***;
  %let maxplen=%length(&reppages);
  %let pxx = %eval(&startcol+&totwidth-&maxplen-&maxplen-9);

%end;

%if "&outfile" ne "" %then %do;
  %if %upcase(&filetype) = HTML %then %do;
    ods listing close;
    ods html body="&outfile";
    ods html select all;
  %end;
  %if %upcase(&filetype) = RTF %then %do;
    ods listing close;
    ods rtf body="&outfile";
    ods rtf select all;
  %end;
  %if %upcase(&filetype) = PDF %then %do;
    ods listing close;
    ods pdf body="&outfile";
    ods pdf select all;
  %end;
  %else %if %upcase(&filetype) = ASCII %then %do;
    proc printto new print="&outfile";
    run;
  %end;
%end;

%if "&outds" ne "" %then %do;
 data &outds;
  set _XXDATA2;
 run;
%end;


proc report data=_xxdata2 nowd headline headskip split="&split" spacing=&spacing missing;
  column %if &pagenum=Y %then _lspage_; &by 
         %if &any=Y %then anyflag;
         %if &order=FREQ %then ord1; &var1 _level_ 
         %if &varcnt>1 %then %do;
           %do i=2 %to &varcnt;
             %if &order=FREQ %then ord&i; &&var&i 
           %end;
         %end;
         %if &flow ne %then _xlevx_;
         _desc_ %do i = 1 %to &numcols;
                  %if &i=1 %then %do;
                    %do j = 1 %to %eval(&colcnt-1);
                      ("-&&cl&j._&i.-"
                      %let last&j = &&cl&j._&i;
                      %let lastlev=&j;
                      %put Setting last&j to "&&cl&j._&i";
                    %end;
                    col&i
                  %end;
                  %else %do;
                    %do j = 1 %to %eval(&colcnt-1);
                      %put i=&i , j=&j , Comparing last&j (=&&last&j) with cl&j._&i (=&&cl&j._&i) ;
                      %if %trim("&&last&j") ne %trim("&&cl&j._&i") %then %do;
                        %if &lastlev ge &j %then %do;
                          %do k=1 %to %eval(&colcnt-&j);
                            %put putting bracket number &k;
                            )
                          %end;
                        %end;
                        ("-&&cl&j._&i.-"
                        %let last&j = &&cl&j._&i;
                        %let lastlev = &j;
                        %put setting last&j equal to "&&cl&j._&i";
                      %end;
                      %else %do;
                        %put %trim("&&last&j") and %trim("&&cl&j._&i") are supposed to be equal!!!;
                      %end;
                    %end;
                    col&i
                  %end;
                  %if &i=&numcols %then %do;
                    %do j = 1 %to %eval(&colcnt-1);
                      )
                    %end;
                  %end;
                %end;
         %if &total=Y %then %do;
           totcol
         %end;;
  %if &pagenum=Y %then %do;
    define _lspage_ / group noprint;
  %end;
  %do i=1 %to &bycnt;
    define &&by&i / order;
  %end;
  %if &any=Y %then %do;
    define anyflag / order noprint;
  %end;
  %if &order=FREQ %then %do;
    define ord1 / order descending noprint;
  %end;
  %if &flow ne %then %do;
    define _xlevx_ / order noprint;
  %end;
  define &var1 / order noprint width=&dlen;
  define _level_ / order noprint;
  %if &varcnt>1 %then %do;
    %do i=2 %to &varcnt;
      %if &order=FREQ %then %do;
        define ord&i / order descending noprint;
      %end;
      define &&var&i / order noprint;
    %end;
  %end;
  define _desc_ / display width=&dlen "&eventlbl" left;
  %do i = 1 %to &numcols;
    define col&i / display "&&cl&colcnt._&i" center width=&&clen&i;
  %end;
  %if &total=Y %then %do;
    define totcol / display "&tothead" center;
  %end;
  %if &skip ne %then %do;
    break after &&var&skip / skip;
  %end;
  %if &pagenum=Y %then %do;
    break after _lspage_ / page;
    compute after _lspage_;
      %if %upcase(&filetype) = ASCII %then %do;
        length uline $ &totwidth;
        uline=repeat('-',&totwidth);
        line @&startcol uline $&totwidth..;
      %end;
      line @&pxx "Page " _lspage_ &maxplen.. " of %trim(&reppages)";
    endcomp;
  %end;
run;

%if "&outfile" ne "" %then %do;
  %if %upcase(&filetype) = HTML %then %do;
    ods html close;
  %end;
  %else %if %upcase(&filetype) = RTF %then %do;
    ods rtf close;
  %end;
  %else %if %upcase(&filetype) = PDF %then %do;
    ods pdf close;
  %end;
  %else %if %upcase(&filetype) = ASCII %then %do;
    proc printto;
    run;
  %end;
%end;

%if %length(&gencode) > 0 %then %do;
  data _null_;
    file gencode mod;
    %if "&outfile" ne "" %then %do;
      %if %upcase(&filetype) = HTML %then %do;
        put "ods listing close;";
        put 'ods html body="' @;
        put "&outfile" @;
        put '";';
        put "ods html select all;";
      %end;
      %else %if %upcase(&filetype) = RTF %then %do;
        put "ods listing close;";
        put 'ods rtf body="' @;
        put "&outfile" @;
        put '";';
        put "ods rtf select all;";
      %end;
      %else %if %upcase(&filetype) = PDF %then %do;
        put "ods listing close;";
        put 'ods pdf body="' @;
        put "&outfile" @;
        put '";';
        put "ods pdf select all;";
      %end;
      %else %if %upcase(&filetype) = ASCII %then %do;
        put 'proc printto new print="' @;
        put "&outfile" @;
        put '";';
        put "run;";
      %end;
    %end;
    put "**********************;";
    put "* Do the Proc Report *;";
    put "**********************;";
    put 'proc report data=_xxdata2 nowd headline headskip split="' @;
    put "&split" @;
    put '" spacing=' @;
    put "&spacing missing;";
    put "  column " @;
    %if &pagenum=Y %then %do;
      put "_lspage_ " @;
    %end;
    put "&by " @;
    %if &any=Y %then %do;
      put "anyflag " @;
    %end;
    %if &order=FREQ %then %do;
      put "ord1 " @;
    %end;
    put "&var1 _level_ " @;
    %if &varcnt>1 %then %do;
      %do i=2 %to &varcnt;
        %if &order=FREQ %then %do;
          put "ord&i " @;
        %end;
        put "&&var&i " @;
      %end;
    %end;
    %if &flow ne %then %do;
      put "_xlevx_ " @;
    %end;
    put "_desc_ " @;
    %do i = 1 %to &numcols;
      %if &i=1 %then %do;
        %do j = 1 %to %eval(&colcnt-1);
          put '("-' @;
          put "&&cl&j._&i.-" @;
          put '" ' @;
          %let last&j = &&cl&j._&i;
          %let lastlev=&j;
        %end;
        put "col&i " @;
      %end;
      %else %do;
        %do j = 1 %to %eval(&colcnt-1);
          %if %trim("&&last&j") ne %trim("&&cl&j._&i") %then %do;
            %if &lastlev ge &j %then %do;
              %do k=1 %to %eval(&colcnt-&j);
                put ")" @;
              %end;
            %end;
            put '("-' @;
            put "&&cv&j._&i.-" @;
            put '" ' @;
            %let last&j = &&cv&j._&i;
            %let lastlev = &j;
          %end;
        %end;
        put "col&i " @;
      %end;
      %if &i=&numcols %then %do;
        %do j = 1 %to %eval(&colcnt-1);
          put ") " @;
        %end;
      %end;
    %end;
    %if &total=Y %then %do;
      put "totcol " @;
    %end;
    put ";";
    %if &pagenum=Y %then %do;
      put "  define _lspage_ / group noprint;";
    %end;
    %do i=1 %to &bycnt;
      put "  define &&by&i / order;";
    %end;
    %if &any=Y %then %do;
      put "  define anyflag / order noprint;";
    %end;
    %if &order=FREQ %then %do;
      put "  define ord1 / order descending noprint;";
    %end;
    %if &flow ne %then %do;
      put "  define _xlevx_ / order noprint;";
    %end;
    put "  define &var1 / order noprint;";
    put "  define _level_ / order noprint;";
    %if &varcnt>1 %then %do;
      %do i=2 %to &varcnt;
        %if &order=FREQ %then %do;
        put "  define ord&i / order descending noprint;";
        %end;
        put "  define &&var&i / order noprint;";
      %end;
    %end;
    put "  define _desc_ / display width=&dlen " @;
    put '"' @;
    put "&eventlbl" @;
    put '" left;';
    %do i = 1 %to &numcols;
      put "  define col&i / display " @;
      put '"' @;
      put "&&cl&colcnt._&i" @;
      put '" center width=' @;
      put "&&clen&i;";
    %end;
    %if &total=Y %then %do;
      put '  define totcol / display "' @;
      put "&tothead" @;
      put '" center;';
    %end;
    %if &skip ne %then %do;
      put "  break after &&var&skip / skip;";
    %end;
    %if &pagenum=Y %then %do;
      put "  * put the Page XX of XX details;";
      put "  break after _lspage_ / page;";
      put "  compute after _lspage_;";
      %if %upcase(&filetype) = ASCII %then %do;
        put "    length uline $ &totwidth;";
        put "    uline=repeat('-',&totwidth);";
        put "    line @&startcol uline $&totwidth..;";
      %end;
      put "    line @&pxx " @;
      put '"Page " _lspage_ ' @;
      put "&maxplen.. " @;
      put '" of ' @;
      put "%trim(&reppages)" @;
      put '";';
      put "  endcomp;";
    %end;
    put "run;";
    put;
    %if "&outfile" ne "" %then %do;
      %if %upcase(&filetype) = HTML %then %do;
        put "ods html close;";
      %end;
      %else %if %upcase(&filetype) = RTF %then %do;
        put "ods rtf close;";
      %end;
      %else %if %upcase(&filetype) = PDF %then %do;
        put "ods pdf close;";
      %end;
      %else %if %upcase(&filetype) = ASCII %then %do;
        put "proc printto";
        put "run;";
      %end;
    %end;
  run;
%end;

%exit:

%mend doevents;

